/*
 * init_lhdfs - ial the lhdfs
 * @param uri      the palce where is the filesystem placed
 * @param fs_name  the name of the filesystem
 * @return Return a handle to the lhdfs
 */
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <glib.h>
#include "hlfs.h"
#include "misc.h"
#include "comm_define.h"
#include "storage.h"
#include "storage_helper.h"
#include "bs_local.h"


int read_fs_superblock(struct back_storage *storage,struct super_block *sb){
    int ret = 0;
    if(0!=storage->bs_file_is_exist(storage,"superblock")){
        g_message( "superblock file can not be accessable \n");
        ret = -1;
        goto out;
    }
    char key_file_buf[4096];
    bs_file_t file = storage->bs_file_open(storage,"superblock",BS_READONLY);
    if(file == NULL){
      g_message("can not open superblock file\n");  
      goto out1;
    }
    int size = storage->bs_file_pread(storage,file,key_file_buf,4096,0);
    if(size <0){
      g_message("can not read superblock file\n");
      goto out1;
    }
    GKeyFile * sb_keyfile = g_key_file_new();
    if(FALSE == g_key_file_load_from_data(sb_keyfile,key_file_buf,size,G_KEY_FILE_NONE,NULL)){
       g_message( "superblock file format is not key value pairs \n");
       ret = -2;
       goto out2;
    }
    gchar * fs_name =  g_key_file_get_string(sb_keyfile,"METADATA","fs_name",NULL);
    guint32 seg_size = g_key_file_get_integer(sb_keyfile,"METADATA","segment_size",NULL);
    guint32 block_size = g_key_file_get_integer(sb_keyfile,"METADATA","block_size",NULL);
    if(fs_name==NULL || seg_size == 0 || block_size ==0 ){
       g_message( "superblock parse error \n");
       g_free(fs_name);
       ret = -3;
       goto out2;
    }
    sb->seg_size = seg_size;
    sb->block_size = block_size;
    g_strlcpy(sb->fsname,fs_name,MAX_FILE_NAME_LEN);
    g_message( "superblock : seg size :%u block size :%d fsname :%s\n",seg_size,block_size,fs_name);

out2:
    g_key_file_free(sb_keyfile);
out1:
    storage->bs_file_close(storage,file);
out:
    return ret;
}

#if 1
static int get_latest_write_info(struct back_storage *storage, guint64* segno, guint64 *offset){
    int ret = 0;
    int num_entries;
    bs_file_info_t * infos = storage->bs_file_list_dir(storage,".",&num_entries);
    if(infos == NULL){
       g_message("can not get fs:%s seg entries\n",storage->fs_name);
       return -1;
    }
    int i=0;
    gchar * latest_file = NULL;
    uint64_t latest_st_mtime = 0;
    bs_file_info_t * info = infos;
    for(i=0;i<num_entries;i++){
        g_message("file:%s,size:%d,time:%ld\n",info->name,info->size,info->lmtime);  
        if(g_str_has_suffix(info->name,"seg")){
             if(latest_st_mtime < info->lmtime){
                latest_st_mtime = info->lmtime;
                latest_file = info->name;
                *offset = info->size;
            }
        } 
        info++;
    }

    if(latest_file!=NULL){
        g_message("latest file\n",latest_file);
        gchar *basename = g_basename(latest_file);
        gchar **v = g_strsplit(basename,".",2);
        *segno = atol(v[1]);
        g_strfreev(v);
    }else{
        *segno = 0;
    }
out:
    //g_free(latest_file);
    //g_free(filename);
    free(infos);
    return ret;
}
#endif

#if 1
static int load_latest_inode_map_entry(struct hlfs_ctrl * ctrl){
   int ret = 0;
   char *segfile_name = build_segfile_name(ctrl->last_segno);
   bs_file_t file = ctrl->storage->bs_file_open(ctrl->storage,segfile_name,BS_READONLY); 
   if(file==NULL){
      g_message("3 --------------> can not open segment file %s\n",segfile_name);
      return -1; 
   }

   uint64_t inode_map_entry_pos = ctrl->last_offset - 
                        sizeof(struct inode_map_entry);
   g_message("inode map entry pos %llu\n",inode_map_entry_pos);
   if(sizeof(struct inode_map_entry) != 
            ctrl->storage->bs_file_pread(ctrl->storage,
                 file,&ctrl->imap_entry,
                 sizeof(struct inode_map_entry),
                 inode_map_entry_pos)){
       g_message("can not read inode map entry \n");
       ret = -1;
       goto out1;
    }
out1:
    ctrl->storage->bs_file_close(ctrl->storage,file);
out2:
    g_free(segfile_name);
    //g_free(segfile_path);
    return ret;
}
#endif

uint64_t  SEGMENT_SIZE_MASK = 0;
uint64_t  SEGMENT_SIZE_SHIFT = 0;

struct hlfs_ctrl *
init_hlfs(const char *uri, const char *fs_name)
{
    int ret;
    struct hlfs_ctrl *ctrl = (struct hlfs_ctrl*)g_malloc0(sizeof(struct hlfs_ctrl));
    if(uri == NULL && fs_name == NULL){
        g_message( "input error \n");
        return NULL;
    }
    struct back_storage *storage = init_storage_handler(uri,fs_name);
    if( storage == NULL){
        g_message( "[uri:%s] [fs:%s] can not accessable\n",uri,fs_name);
        g_free(ctrl);
        return NULL;
    }
    g_message( "storage name:%s,fsname:%s location:%s\n",(char*)storage->storage_name,storage->fs_name,storage->fs_location);
    ctrl->storage = storage;
    if(0!= read_fs_superblock(ctrl->storage,&ctrl->sb)){
            g_message( "[uri:%s] [fs:%s] read superblock failed\n",uri,fs_name);
            g_free(ctrl);
            ctrl = NULL;
            goto out;
    }


    guint64  segno=0;
    guint64  offset = 0;
    if( 0!=get_latest_write_info(ctrl->storage,&segno,&offset)){
        g_free(ctrl);
        ctrl = NULL;
        goto out;
    }
    g_message( "segno %llu,offset %llu\n",segno,offset);
    ctrl->last_segno = segno;
    ctrl->last_offset = offset;
    if(ctrl->last_segno != 0 || ctrl->last_offset != 0){
        if(0!=load_latest_inode_map_entry(ctrl)){
            g_message( "load inode map entry failed\n");
            ret = -1;
            goto out;
        }
    }
    uint64_t segment_size = ctrl->sb.seg_size;
    SEGMENT_SIZE_MASK  = segment_size - 1;
    SEGMENT_SIZE_SHIFT = 0;
    while (0 != (segment_size = (segment_size >> 1)))
    {                                                                                                         
        SEGMENT_SIZE_SHIFT++;
    }
out:
    return ctrl;
}   
